package net.towee.client.com.repo.presenter;

import java.util.Arrays;
import java.util.List;

import org.apache.poi.hssf.record.formula.functions.N;

import net.towee.client.com.repo.RepositoryNodeSelector;
import net.towee.client.com.repo.RepositoryShower;
import net.towee.client.com.repo.RepositoryTreeNodeShower;
import net.towee.client.event.core.NodeReceivedEvent;
import net.towee.client.event.core.NodeReceivedHandler;
import net.towee.client.event.core.NodesRemovedEvent;
import net.towee.client.event.core.NodesRemovedHandler;
import net.towee.client.event.core.NodesTrashEvent;
import net.towee.client.event.core.NodesTrashHandler;
import net.towee.client.managers.AccountHelper;
import net.towee.client.managers.NodesManager;
import net.towee.model.account.AccountPermission;
import net.towee.model.repo.NodeData;

import com.google.common.base.Preconditions;
import com.google.gwt.event.logical.shared.HasOpenHandlers;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;

public class RepositoryTreeNodePresenter implements RepositoryTreeNodeShower, OpenHandler<NodeData>,
		ValueChangeHandler<NodeData> {

	public interface Display extends IsWidget {
		void showNode(NodeData node, boolean canRemove, boolean canRename);

		void removeNode(NodeData node);

		List<NodeData> getSelectedNodes();

		HasWidgets getPathSelectorContainer();

		HasOpenHandlers<NodeData> getOpenNodeEvents();

		HasValueChangeHandlers<NodeData> getDeleteEvents();

		void noNodesMessage();

		void startLoadingNodes();

		void endLoadingNodes();
	}

	// Injected
	private final Display display;
	private final NodesManager nodesManager;
	private final HandlerManager eventBus;
	private final RepositoryShower shower;
	private final RepositoryNodeSelector selector;
	private final AccountHelper account;

	// Current active node
	private NodeData currentNode;

	@Inject
	public RepositoryTreeNodePresenter(Display display, NodesManager nodesManager,
			HandlerManager eventBus, RepositoryShower shower, RepositoryNodeSelector selector,
			AccountHelper account) {
		this.display = display;
		this.nodesManager = nodesManager;
		this.eventBus = eventBus;
		this.shower = shower;
		this.selector = selector;
		this.account = account;

		// Bind events
		bindEvents();
	}

	private void bindEvents() {
		display.getOpenNodeEvents().addOpenHandler(this);
		display.getDeleteEvents().addValueChangeHandler(this);

		eventBus.addHandler(NodeReceivedEvent.TYPE, new NodeReceivedHandler() {
			@Override
			public void onNodesReceived(List<NodeData> nodes) {
				showNodes(nodes);
			}
		});
		
		eventBus.addHandler(NodesTrashEvent.TYPE, new NodesTrashHandler() {
			@Override
			public void onNodesToTrash(List<NodeData> nodes) {
				removeNodes( nodes );
			}			
		});
		
		eventBus.addHandler(NodesRemovedEvent.TYPE, new NodesRemovedHandler() {
			@Override
			public void onNodesRemoved(List<NodeData> nodes) {
				removeNodes( nodes );
			}
		});

		selector.addSelectionHandler(new SelectionHandler<NodeData>() {
			@Override
			public void onSelection(SelectionEvent<NodeData> event) {
				shower.showTreeNode(event.getSelectedItem());
			}
		});
	}

	@Override
	public List<NodeData> getSelectedNodes() {
		if (display.getSelectedNodes().isEmpty()) {
			return Arrays.asList(currentNode);
		} else {
			return display.getSelectedNodes();
		}
	}

	@Override
	public NodeData getShowedNode() {
		return currentNode;
	}

	@Override
	public void setNode(NodeData node) {
		this.currentNode = node;
	}

	@Override
	public void removeNodes(List<NodeData> cutNodes) {
		for (NodeData node : cutNodes) {
			display.removeNode(node);
		}
	}

	@Override
	public void showNodes(List<NodeData> cutNodes) {		
		if (cutNodes.size() == 0)
			display.noNodesMessage();

		for (NodeData node : cutNodes) {
			display.showNode(node, account.hasPermissions(node.getGroupId(), AccountPermission.CAN_PUT_TO_TRASH),
					account.hasPermissions(node.getGroupId(), AccountPermission.CAN_RENAME_NODES));
		}
	}

	@Override
	public void go(HasWidgets container) {
		// We must set current node first
		Preconditions.checkNotNull(currentNode);
		display.startLoadingNodes();

		// Get nodes
		if (currentNode.getId() != null) {
			nodesManager.getSubTree(currentNode.getGroupId(), currentNode.getId(),
					new AsyncCallback<List<NodeData>>() {
						@Override
						public void onFailure(Throwable caught) {
						}

						@Override
						public void onSuccess(List<NodeData> result) {
							display.endLoadingNodes();
							showNodes(result);
						}
					});
		} else {
			nodesManager.getRootNodes(currentNode.getGroupId(), new AsyncCallback<List<NodeData>>() {
				@Override
				public void onFailure(Throwable caught) {
				}

				@Override
				public void onSuccess(List<NodeData> result) {
					display.endLoadingNodes();
					showNodes(result);
				}
			});
		}

		// Display shower
		container.clear();
		container.add(display.asWidget());

		// Select node
		selector.setCurrentNode(currentNode);
		selector.go(display.getPathSelectorContainer());
	}

	/**
	 * On delete some node
	 */
	@Override
	public void onValueChange(final ValueChangeEvent<NodeData> event) {		
		// Remove from server
		nodesManager.putToTrash(event.getValue().getGroupId(), Arrays.asList( event.getValue().getId() ), new AsyncCallback<Boolean>(){
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {	
				// Remove from interface
				display.removeNode( event.getValue() );
			}
		});
	}

	/**
	 * On open some node
	 */
	@Override
	public void onOpen(OpenEvent<NodeData> event) {
		shower.showTreeNode(event.getTarget());
	}
}
